﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FinPlusStatistics
{
    public static class Statistics
    {
        private static double DegreeToRadian(double angle)
        {
            return Math.PI * angle / 180.0;
        }

        public static double Variance(this IEnumerable<double> source)
        {
            var n = 0;
            var mean = 0.0;
            var M2 = 0.0;

            foreach (double x in source)
            {
                n++;
                var delta = x - mean;
                mean = mean + delta / n;
                M2 += delta * (x - mean);
            }
            return M2 / (n - 1);
        }

        public static double RecentAverage(this double[] source, long num)
        {
            double res = double.NaN;
            var last = source.Length - 1;
            var total = 0.0;

            if (last - num < 0) return res;

            for (int n = last; n > last - num; n--)
                total += source[n];

            return total / num;
        }

        public static double RegressMaxChange(this double[] source, long step, bool negativeChg = false)
        {
            var res = 0.0;
            var n = 0;
            var l = source.Length - 1;

            for (n = l; n >= 0; n--)
            {
                var i = n - step;
                
                if (i <= 0) break;

                var change =  source[n] - source[i];

                if (negativeChg && change < 0 && change < res)
                    res = change;
                else if (!negativeChg && change > 0 && change > res)
                    res = change;

            }

            return res;
        }

        public static double StandardDeviation(this IEnumerable<double> source)
        {
            return Math.Sqrt(source.Variance());
        }

        public static double Median(this IEnumerable<double> source)
        {
            var sortedList = from number in source orderby number select number;

            var count = sortedList.Count();
            var itemIndex = count / 2;
            if (count % 2 == 0)
                return (sortedList.ElementAt(itemIndex) + sortedList.ElementAt(itemIndex - 1)) / 2;

            return sortedList.ElementAt(itemIndex);
        }

        public static T? Mode<T>(this IEnumerable<T> source) where T : struct
        {
            var sortedList = from number in source orderby number select number;

            var count = 0;
            var max = 0;
            T current = default(T);
            T? mode = new T?();

            foreach (T next in sortedList)
            {
                if (current.Equals(next) == false)
                {
                    current = next;
                    count = 1;
                }
                else
                {
                    count++;
                }

                if (count > max)
                {
                    max = count;
                    mode = current;
                }
            }

            if (max > 1)
                return mode;

            return null;
        }

        public static double Range(this IEnumerable<double> source)
        {
            return source.Max() - source.Min();
        }

        public static double Covariance(this IEnumerable<double> source, IEnumerable<double> other)
        {
            var len = source.Count();

            var avgSource = source.Average();
            var avgOther = other.Average();
            var covariance = 0.0;

            for (int i = 0; i < len; i++)
                covariance += (source.ElementAt(i) - avgSource) * (other.ElementAt(i) - avgOther);

            return covariance / len;
        }

        public static double Pearson(this IEnumerable<double> source, IEnumerable<double> other)
        {
            return source.Covariance(other) / (source.StandardDeviation() * other.StandardDeviation());
        }
    }
}
